﻿using System.Threading;
using System.Web;
using NHibernate;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Modules;
using SportStore.Core.Data;
using SportStore.Core.Http;
using SportStore.Core.NHibernate;
using HttpRequestWrapper = SportStore.Core.Http.HttpRequestWrapper;
using HttpResponseWrapper = SportStore.Core.Http.HttpResponseWrapper;

namespace SportStore.Core.IoC
{
    public class InjectionModule : NinjectModule
    {
        public override void Load()
        {
            // Http
            this.Bind<HttpContextBase>().ToMethod(c => new HttpContextWrapper(HttpContext.Current)).InRequestScope();
            this.Bind<IHttpRequest>().ToMethod(c => new HttpRequestWrapper(HttpContext.Current == null ? null : HttpContext.Current.Request)).InRequestScope();
            this.Bind<IHttpResponse>().ToMethod(c => new HttpResponseWrapper(HttpContext.Current == null ? null : HttpContext.Current.Response)).InRequestScope();

            // Data Access
            this.Bind<ISessionFactoryBuilder>().To<SessionFactoryBuilder>().InSingletonScope();
            this.Bind<ISessionFactory>().ToMethod(context => context.Kernel.Get<ISessionFactoryBuilder>().CreateSessionFactory()).InSingletonScope();
            this.Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InScope(context => this.GetRequestOrThreadScopeObject());
            this.Bind<IStatelessSession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenStatelessSession()).InScope(context => this.GetRequestOrThreadScopeObject());
            this.Bind<IUnitOfWork>().To<UnitOfWork>().InScope(context => this.GetRequestOrThreadScopeObject());

            // Repositories injection
            var repositoryScanner = new AssemblyScanner();
            repositoryScanner.FromAssembliesMatching("SportStore.Data.*");
            repositoryScanner.WhereTypeIsInNamespace("SportStore.Data.NHibernate.Repositories");
            repositoryScanner.BindWith<DefaultBindingGenerator>();
            repositoryScanner.InScope(context => this.GetRequestOrThreadScopeObject());

            this.Kernel.Scan(repositoryScanner);

            // Services injection
            var serviceScanner = new AssemblyScanner();
            serviceScanner.FromAssembliesMatching("SportStore.Services.*");
            serviceScanner.WhereTypeIsInNamespace("SportStore.Services");
            serviceScanner.BindWith<DefaultBindingGenerator>();
            serviceScanner.InScope(context => this.GetRequestOrThreadScopeObject());

            this.Kernel.Scan(serviceScanner);

            // Controllers injection
            var controllerScanner = new AssemblyScanner();
            controllerScanner.FromAssembliesMatching(new[] { "SportStore.*" });
            controllerScanner.WhereTypeIsInNamespace("SportStore.Controllers");
            controllerScanner.BindWith<SelfBinder>();
            serviceScanner.InScope(context => this.GetRequestOrThreadScopeObject());

            this.Kernel.Scan(controllerScanner);
        }

        #region Methods

        /// <summary>
        /// Gets the request or thread scope object.
        /// </summary>
        protected object GetRequestOrThreadScopeObject()
        {
            return HttpContext.Current != null ? (object)HttpContext.Current.Request : Thread.CurrentThread;
        }

        #endregion
    }
}
